﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.ServiceModel.Security;
using System.Web;
using System.Web.Caching;
using System.Web.Configuration;
using System.Web.Services;

namespace App_Code
{
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class MinerHandler : WebService
    {
        [WebMethod]
        public StartGameResult StartGame(string login, string password, int xSize, int ySize, int mineCount)
        {
            byte[] res;
            Game game;
            using (var dc = CreateDataContext())
            {
                var user = (from u in dc.Users
                            where u.Login == login && u.PasswordHash == password
                            select u).FirstOrDefault();
                if (user == null)
                    throw new SecurityAccessDeniedException();

                var resBoard = new MineBoard(xSize, ySize, mineCount);
                res = resBoard.ToByteArray();
                game = new Game
                           {
                               StartTime = DateTime.Now,
                               ID = Guid.NewGuid(),
                               State = 0,
                               XSize = (byte) xSize,
                               YSize = (byte) ySize,
                               MineCount = (byte) mineCount,
                               User = user,
                               BoardState = res
                           };
                CacheGame(game);
                dc.Games.InsertOnSubmit(game);
                dc.SubmitChanges(ConflictMode.FailOnFirstConflict);
            }
            return new StartGameResult
                       {
                           GameID = game.ID,
                       };
        }

        [WebMethod]
        public CellOperationResult CellOperation(Guid gameId, int x, int y, int operationId)
        {
            if (operationId < 0 || operationId > OPERATION_TITLE.Length)
                throw new ArgumentException("Uknown operation identifier " + operationId);

            var res = new CellOperationResult();

            using (var dc = CreateDataContext())
            {
                var game = GetCache()["GAME_" + gameId] as Game;
                if (game == null)
                {
                    game = (from g in dc.Games where g.ID == gameId select g).SingleOrDefault();
                    if (game == null)
                        throw new SecurityAccessDeniedException("You have no access to this game");
                    CacheGame(game);
                }

                

                if (game.MineBoard[x, y].IsOpened())
                    throw new InvalidOperationException(string.Format("Could not perform operation {0} on opened cell", operationId));

                switch (operationId)
                {
                    case 0:
                        var logs = new List<GameLogEntry>(1)
                                       {
                                           new GameLogEntry
                                               {
                                                   ActionKind = 0,
                                                   GameID = gameId,
                                                   Time = DateTime.Now,
                                                   X = (byte) x,
                                                   Y = (byte) y
                                               }
                                       };
                        var openedCells = new List<Cell>();
                        try
                        {
                            res.ResultCode = game.MineBoard.Open(x, y,
                                                                 (ox, oy) => logs.Add(
                                                                                 new GameLogEntry
                                                                                     {
                                                                                         ActionKind = 1,
                                                                                         GameID = gameId,
                                                                                         Time = DateTime.Now,
                                                                                         X = (byte) ox,
                                                                                         Y = (byte) oy
                                                                                     }));
                            if (res.ResultCode == 0)
                                res.OpenedCells = openedCells.ToArray();

                        }
                        finally
                        {
                            dc.GameLogEntries.InsertAllOnSubmit(logs);
                            dc.SubmitChanges(ConflictMode.FailOnFirstConflict);
                        }

                        break;

                    case 1:
                        throw new NotImplementedException();
                        

                    case 2:
                        throw new NotImplementedException();

                    case 3:
                        throw new NotImplementedException();

                    case 4:
                        throw new NotImplementedException();
                }
            }

            return res;
        }

        private void CacheGame(Game game)
        {
            GetCache().Add("GAME_" + game.ID, game, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5), CacheItemPriority.Normal, null);
        }

        private DBClasses CreateDataContext()
        {
            return new DBClasses(WebConfigurationManager.ConnectionStrings["MINERConnectionString"].ConnectionString);
        }

        private Cache GetCache()
        {
            return HttpContext.Current.Cache;
        }

        private static readonly string[] OPERATION_TITLE = new[] {"OPEN", "FLAG", "REMOVE_FLAG", "QUESTION", "REMOVE_QUESTION"};
    }

    public class StartGameResult
    {
        //public byte[] BoardState;
        public Guid GameID;
    }

    public class CellOperationResult
    {
        public Cell[] OpenedCells;
        public int ResultCode; // 0 - OK. 1 - Victory. 2 - Explosion
    }

    public class Cell
    {
        public int X;
        public int Y;
        public int Value;
    }
}